#ifndef __TRI_MESH_H__
#define __TRI_MESH_H__

#include <vector>
using namespace std;

#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
using boost::noncopyable;
using boost::shared_ptr;

#define L_TRI_MESH_H
#include <llib.h>
using namespace llib;
using namespace llib::geom;


class ModelEdge;
class ModelFace;
class TriMeshModel;
typedef shared_ptr<ModelEdge>   ModelEdgePtr;
typedef shared_ptr<ModelFace>   ModelFacePtr;

class ModelEdge : noncopyable {
private:
    /* Constructor */
    ModelEdge(Vec3f& vertexBegin, Int32 vertexIndexBegin,
              Vec3f& vertexEnd,   Int32 vertexIndexEnd,
              Int32 edgeIndex) :
        VertexBegin(vertexBegin), VertexIndexBegin(vertexIndexBegin),
        VertexEnd  (vertexEnd),   VertexIndexEnd  (vertexIndexEnd),
        EdgeIndex  (edgeIndex) {}

public:
    /* line index */
    const Int32     EdgeIndex;

    Vec3f&          VertexBegin;
    const Int32     VertexIndexBegin;
    Vec3f&          VertexEnd;
    const Int32     VertexIndexEnd;

    /* friends */
    friend class TriMeshModel;
};

class ModelFace : noncopyable {
private:
    /* Constructor */
    ModelFace(Vec3f& vertexA, Int32 vertexIndexA,
              Vec3f& vertexB, Int32 vertexIndexB,
              Vec3f& vertexC, Int32 vertexIndexC,
              Int32 faceIndex) :
        VertexA(vertexA), VertexIndexA(vertexIndexA),
        VertexB(vertexB), VertexIndexB(vertexIndexB),
        VertexC(vertexC), VertexIndexC(vertexIndexC),
        FaceIndex(faceIndex) {}

public:
    /* face index */
    const Int32     FaceIndex;

    Vec3f&          VertexA;
    const Int32     VertexIndexA;
    Vec3f&          VertexB;
    const Int32     VertexIndexB;
    Vec3f&          VertexC;
    const Int32     VertexIndexC;

    /* friends */
    friend class TriMeshModel;
};

/* edge->face enum */
enum FaceType {
    FaceLeft = 0,
    FaceRight = 1
};
/* face->edge enum */
enum EdgeType {
    EdgeAB = 0,
    EdgeBC = 1,
    EdgeCA = 2
};

class TriMeshModel {
private:
    /* vertex list */
    Vec3f*  _vertexList;                /* input */
    Int32   _vertexNum;
    /* face vertex/edge index list */
    Vec3i*  _faceVertexIndexList;       /* input */
    Vec3i*  _faceEdgeIndexList;
    Int32   _faceNum;
    /* edge vertex/face index list */
    Vec2i*  _edgeVertexIndexList;
    Vec2i*  _edgeFaceIndexList;
    Int32   _edgeNum;
    /* model build */
    Boolean _modelBuild;

    /* private functions */
    void resetModelData(vector<Vec3f>& vertexList, vector<Vec3i>& faceVertexIndexList);
    void modelCopy(const TriMeshModel& m);

public:
    /* Constructor : with no data set */
    TriMeshModel();
    /* Constructor : input vertex list and face vertex index list */
    TriMeshModel(vector<Vec3f>& vertexList, vector<Vec3i>& faceVertexIndexList);
    /* Constructor : copy construction */
    TriMeshModel(const TriMeshModel& m);
    /* Destructor : delete all lists */
    ~TriMeshModel();

    /* assignment operator */
    TriMeshModel& operator=(const TriMeshModel& m);

    /* set model deta */
    void            SetModelData(vector<Vec3f>& vertexList, vector<Vec3i>& faceVertexIndexList);

    /* vertex query */
    Vec3f&          GetVertex(Int32 index);
    const Int32     GetVertexNum() { return _vertexNum; }

    /* edge query */
    ModelEdgePtr    GetEdge(Int32 index);
    ModelEdgePtr    GetEdge(ModelFacePtr facePtr, EdgeType edgeType);
    const Int32     GetEdgeNum() { return _edgeNum; }

    /* face query */
    const static Int32 InvalidFaceIndex = -1;
    ModelFacePtr    GetFace(Int32 index);
    ModelFacePtr    GetFace(ModelEdgePtr edgePtr, FaceType faceType);
    const Int32     GetFaceNum() { return _faceNum; }

    /* internal vertex & index array query */
    const Float*    GetVertexList() { return (const Float*)_vertexList; }
    const Int32*    GetFaceVertexIndexList() { return (const Int32*)_faceVertexIndexList; }
};


#endif
